import contextlib
import os
import re
import time
import traceback
import win32gui
import fitz
import pythoncom
import pywintypes
import win32api
import win32com
import win32com.client
import win32con
import win32print
import win32ui
from PIL import Image, ImageWin

from .log_util import get_logger

logger = get_logger()

# 常用纸张大小映射
PAPER_SIZES = {
    "Letter": win32con.DMPAPER_LETTER,
    "A3": win32con.DMPAPER_A3,
    "A4": win32con.DMPAPER_A4,
    "A5": win32con.DMPAPER_A5,
    "B4": win32con.DMPAPER_B4,
    "B5": win32con.DMPAPER_B5,
    "Legal": win32con.DMPAPER_LEGAL,
}

# 页面方向映射
ORIENTATIONS = {
    "纵向": win32con.DMORIENT_PORTRAIT,
    "横向": win32con.DMORIENT_LANDSCAPE,
}

# 打印质量映射
QUALITIES = {
    "高": win32con.DMRES_HIGH,
    "中": win32con.DMRES_MEDIUM,
    "低": win32con.DMRES_LOW,
    "草稿": win32con.DMRES_DRAFT,
}

# 双面打印模式映射
DUPLEX_MODES = {
    "单面打印": win32con.DMDUP_SIMPLEX,
    "双面打印(长边翻转)": win32con.DMDUP_VERTICAL,
    "双面打印(短边翻转)": win32con.DMDUP_HORIZONTAL,
}


# ========== 打印机管理 ==========


def get_system_printers():
    """获取系统安装的所有打印机及其信息"""
    printers = []
    try:
        system_printers = win32print.EnumPrinters(
            win32print.PRINTER_ENUM_LOCAL | win32print.PRINTER_ENUM_CONNECTIONS,
            None,
            2,  # level=2，包含详细信息
        )
        for printer in system_printers:
            name = printer.get("pPrinterName")
            port_name = printer.get("pPortName")
            status_code = printer.get("Status")

            # 尝试从端口名解析IP地址
            ip_match = re.search(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", port_name)
            ip = ip_match.group(0) if ip_match else "N/A"

            status = parse_printer_status(status_code)
            printers.append({"name": name, "ip": ip, "status": status})

        logger.info(f"成功获取到 {len(printers)} 台系统打印机。")
    except Exception as e:
        logger.error(f"获取系统打印机列表失败: {e}")
    return printers


def parse_printer_status(status_code):
    """解析打印机状态码为可读文本"""
    if status_code == 0:
        return "就绪"
    status_map = {
        win32print.PRINTER_STATUS_BUSY: "繁忙",
        win32print.PRINTER_STATUS_DOOR_OPEN: "门被打开",
        win32print.PRINTER_STATUS_ERROR: "错误",
        win32print.PRINTER_STATUS_INITIALIZING: "正在初始化",
        win32print.PRINTER_STATUS_IO_ACTIVE: "I/O激活",
        win32print.PRINTER_STATUS_MANUAL_FEED: "手动进纸",
        win32print.PRINTER_STATUS_NO_TONER: "墨粉不足",
        win32print.PRINTER_STATUS_NOT_AVAILABLE: "不可用",
        win32print.PRINTER_STATUS_OFFLINE: "离线",
        win32print.PRINTER_STATUS_OUT_OF_MEMORY: "内存不足",
        win32print.PRINTER_STATUS_OUTPUT_BIN_FULL: "输出纸盒已满",
        win32print.PRINTER_STATUS_PAGE_PUNT: "页面无法打印",
        win32print.PRINTER_STATUS_PAPER_JAM: "卡纸",
        win32print.PRINTER_STATUS_PAPER_OUT: "缺纸",
        win32print.PRINTER_STATUS_PAPER_PROBLEM: "纸张问题",
        win32print.PRINTER_STATUS_PAUSED: "已暂停",
        win32print.PRINTER_STATUS_PENDING_DELETION: "等待删除",
        win32print.PRINTER_STATUS_PRINTING: "正在打印",
        win32print.PRINTER_STATUS_PROCESSING: "正在处理",
        win32print.PRINTER_STATUS_TONER_LOW: "墨粉少",
        win32print.PRINTER_STATUS_USER_INTERVENTION: "需要用户干预",
        win32print.PRINTER_STATUS_WAITING: "等待中",
        win32print.PRINTER_STATUS_WARMING_UP: "正在预热",
    }
    status_text = [text for code, text in status_map.items() if status_code & code]
    return ", ".join(status_text) if status_text else "未知状态"


def get_printer_status(printer_name):
    """获取单个打印机的实时状态"""
    try:
        h_printer = win32print.OpenPrinter(printer_name)
        printer_info = win32print.GetPrinter(h_printer, 2)
        win32print.ClosePrinter(h_printer)
        return parse_printer_status(printer_info.get("Status"))
    except Exception as e:
        logger.warning(f"获取打印机 '{printer_name}' 状态失败: {e}")
        return "无法获取"


# com类型文件扩展名
WORD_EXTS = [".doc", ".docx"]
EXCEL_EXTS = [".xls", ".xlsx"]
PPT_EXTS = [".ppt", ".pptx"]
PDF_EXTS = [".pdf"]
IMG_EXTS = [".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tif", ".tiff"]


def print_file(printer_name, file_path, params):
    """
    静默打印文件到指定打印机，支持WPS文档、PDF、图片等
    """
    # 独立线程的COM初始化
    pythoncom.CoInitialize()

    try:
        extension = os.path.splitext(file_path)[-1].lower()
        logger.info(f"打印参数为：{params}")
        if extension in WORD_EXTS:
            _print_word(printer_name, file_path, params)
        elif extension in EXCEL_EXTS:
            _print_excel(printer_name, file_path, params)
        elif extension in PDF_EXTS:
            _print_pdf(printer_name, file_path, params)
        elif extension in IMG_EXTS:
            _print_img(printer_name, file_path)
        else:
            logger.warning(f"不支持的文件类型: {file_path}")
    except Exception as e:
        logger.error(f"打印文件失败: {file_path} -> {e}")
    finally:
        pythoncom.CoUninitialize()


def _print_word(printer, file_path, params):
    with temporary_printer_settings(printer, params):
        app = win32com.client.DispatchEx("kwps.Application")  # 或者 wps.Application
        app.Visible = False
        try:
            doc = app.Documents.Open(file_path, ReadOnly=1)
            # 设置打印参数
            app.ActivePrinter = printer

            doc.PrintOut(
                Background=False,
                Copies=params.get("copies", 1),
                PrintToFile=False,
            )
            logger.info(f"[Word] 已发送打印任务: {file_path}")
            time.sleep(1)  # 稳定性等待
            doc.Close(False)
        except Exception as e:
            traceback.print_exc()
            logger.error(f"[Word] 打印失败: {file_path} -> {e}")
        finally:
            app.Quit()


def _print_excel(printer, file_path, params):
    with temporary_printer_settings(printer, params):
        app = win32com.client.DispatchEx("Excel.Application")
        app.Visible = False
        try:
            workbook = app.Workbooks.Open(file_path, ReadOnly=1)
            # app.ActivePrinter = printer

            for sheet in workbook.Sheets:
                sheet.PageSetup.PaperSize = PAPER_SIZES.get(params.get("paper_size"))
                sheet.PageSetup.Orientation = ORIENTATIONS.get(
                    params.get("orientation")
                )

            workbook.PrintOut(
                Copies=params.get("copies", 1),
                ActivePrinter=printer,
            )
            logger.info(f"[Excel] 已发送打印任务: {file_path}")
            time.sleep(1)
            workbook.Close(False)
        except Exception as e:
            traceback.print_exc()
            logger.error(f"[Excel] 打印失败: {file_path} -> {e}")
        finally:
            app.Quit()


def _print_pdf(printer_name, pdf_path, params):
    pdf_document = fitz.open(pdf_path)

    # 获取打印机句柄
    hprinter = win32print.OpenPrinter(printer_name)

    try:
        devmode = apply_printer_settings(printer_name, params)
    except Exception as e:
        logger.error(f"为PDF打印配置DEVMODE失败: {e}")
        pdf_document.close()
        return

    try:
        # 创建打印机DC
        hdc = win32ui.CreateDC()
        hdc.CreatePrinterDC(printer_name)
        win32gui.ResetDC(hdc.GetSafeHdc(), devmode)
        # 获取打印机的DPI
        printer_dpi_x = hdc.GetDeviceCaps(win32con.LOGPIXELSX)
        printer_dpi_y = hdc.GetDeviceCaps(win32con.LOGPIXELSY)

        # 获取可打印区域（单位：像素）
        printable_width = hdc.GetDeviceCaps(win32con.HORZRES)
        printable_height = hdc.GetDeviceCaps(win32con.VERTRES)

        # 开始文档
        hdc.StartDoc(pdf_path)

        for page_num in range(pdf_document.page_count):
            page = pdf_document[page_num]

            # 获取PDF页面原始尺寸（单位：点，1点=1/72英寸）
            page_rect = page.rect
            page_width_inch = page_rect.width / 72.0
            page_height_inch = page_rect.height / 72.0

            # 计算目标尺寸（保持原始比例）
            target_width = int(page_width_inch * printer_dpi_x)
            target_height = int(page_height_inch * printer_dpi_y)

            # 如果页面太大，按比例缩放以适应打印区域
            scale = min(
                printable_width / target_width,
                printable_height / target_height,
                1.0,  # 不放大，只缩小
            )

            final_width = int(target_width * scale)
            final_height = int(target_height * scale)

            # 使用合适的DPI渲染PDF页面
            # matrix参数控制渲染分辨率
            zoom = printer_dpi_x / 72.0 * scale
            matrix = fitz.Matrix(zoom, zoom)
            pix = page.get_pixmap(matrix=matrix)

            # 转换为PIL图像
            img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)

            # 开始页面
            hdc.StartPage()

            # 计算居中位置
            x_offset = (printable_width - final_width) // 2
            y_offset = (printable_height - final_height) // 2

            # 绘制图像
            dib = ImageWin.Dib(img)
            dib.draw(
                hdc.GetHandleOutput(),
                (x_offset, y_offset, x_offset + final_width, y_offset + final_height),
            )

            hdc.EndPage()
        logger.info(f"[PDF] 已发送打印任务: {pdf_path}")
        hdc.EndDoc()
    except Exception as e:
        logger.error(f"打印PDF失败: {e}")
    finally:
        hdc.DeleteDC()
        win32print.ClosePrinter(hprinter)

    pdf_document.close()


def _print_img(printer_name, img_path):
    """静默打印图片文件"""
    try:
        # 检查文件是否存在
        if not os.path.exists(img_path):
            raise FileNotFoundError(f"图片文件不存在: {img_path}")

        # 打开图片
        img = Image.open(img_path)

        # 如果是RGBA模式，转换为RGB（打印机不支持透明通道）
        if img.mode == "RGBA":
            # 创建白色背景
            background = Image.new("RGB", img.size, (255, 255, 255))
            background.paste(img, mask=img.split()[3])  # 使用alpha通道作为蒙版
            img = background
        elif img.mode not in ["RGB", "L"]:  # L是灰度模式
            img = img.convert("RGB")

        # 创建打印机DC
        hdc = win32ui.CreateDC()
        hdc.CreatePrinterDC(printer_name)

        # 获取打印机DPI和可打印区域
        printer_dpi_x = hdc.GetDeviceCaps(win32con.LOGPIXELSX)
        printer_dpi_y = hdc.GetDeviceCaps(win32con.LOGPIXELSY)
        printable_width = hdc.GetDeviceCaps(win32con.HORZRES)
        printable_height = hdc.GetDeviceCaps(win32con.VERTRES)

        # 获取图片DPI（如果没有默认为72）
        img_dpi = img.info.get("dpi", (72, 72))
        if isinstance(img_dpi, (int, float)):
            img_dpi = (img_dpi, img_dpi)

        # 计算图片的实际尺寸（英寸）
        img_width_inch = img.width / img_dpi[0]
        img_height_inch = img.height / img_dpi[1]

        # 计算在打印机上的目标尺寸（像素）
        target_width = int(img_width_inch * printer_dpi_x)
        target_height = int(img_height_inch * printer_dpi_y)

        # 如果图片太大，按比例缩放以适应页面
        scale = min(
            printable_width / target_width,
            printable_height / target_height,
            1.0,  # 不放大，只缩小
        )

        final_width = int(target_width * scale)
        final_height = int(target_height * scale)

        # 计算居中位置
        x_offset = (printable_width - final_width) // 2
        y_offset = (printable_height - final_height) // 2

        # 开始打印
        hdc.StartDoc(os.path.basename(img_path))
        hdc.StartPage()

        # 创建DIB并绘制
        dib = ImageWin.Dib(img)
        dib.draw(
            hdc.GetHandleOutput(),
            (x_offset, y_offset, x_offset + final_width, y_offset + final_height),
        )

        hdc.EndPage()
        hdc.EndDoc()
        hdc.DeleteDC()
        logger.info(f"[IMG] 已发送打印任务: {img_path}")
        return True

    except Exception as e:
        logger.error(f"打印图片失败: {e}")
        return False


def print_with_shell_execute(file_path, printer_name):
    """使用Windows Shell Execute API打印文件"""
    try:
        # 使用win32api.ShellExecute打印
        # 参数: hwnd, operation, file, parameters, directory, show_cmd
        result = win32api.ShellExecute(
            0,  # hwnd
            "print",  # 打印操作
            file_path,  # 文件路径
            f'/d:"{printer_name}"',  # 打印机参数
            ".",  # 工作目录
            0,  # SW_HIDE
        )

        # ShellExecute返回值大于32表示成功
        return result > 32

    except Exception as e:
        logger.error(f"使用ShellExecute打印失败: {e}")
        return False


@contextlib.contextmanager
def temporary_printer_settings(printer_name, params):
    """
    一个上下文管理器，临时应用打印机设置，并在之后恢复。
    这是为COM应用（如Word）设计的。
    """
    pDefaults = {"DesiredAccess": win32print.PRINTER_ALL_ACCESS}
    h_printer = win32print.OpenPrinter(printer_name, pDefaults)
    try:
        # 1. 保存原始设置
        original_properties = win32print.GetPrinter(h_printer, 2)

        # 2. 创建并应用新设置
        new_devmode = apply_printer_settings(printer_name, params)
        properties = win32print.GetPrinter(h_printer, 2)
        properties["pDevMode"] = new_devmode
        win32print.SetPrinter(h_printer, 2, properties, 0)
        logger.info(f"已临时为'{printer_name}'应用打印设置: {params}")

        yield  # 在这里执行打印操作

    finally:
        # 3. 恢复原始设置
        win32print.SetPrinter(h_printer, 2, original_properties, 0)
        win32print.ClosePrinter(h_printer)
        logger.info(f"已恢复'{printer_name}'的原始打印设置。")


def apply_printer_settings(printer_name, params):
    """应用打印机设置参数"""
    try:
        # 打开打印机
        h_printer = win32print.OpenPrinter(printer_name)

        # 获取打印机的DEVMODE结构
        properties = win32print.GetPrinter(h_printer, 2)
        devmode = properties["pDevMode"]

        if devmode is None:
            # 如果没有DEVMODE，创建一个新的
            devmode = pywintypes.DEVMODEType()

        # 设置纸张大小
        paper_size = params.get("paper_size")
        if paper_size and paper_size in PAPER_SIZES:
            devmode.PaperSize = PAPER_SIZES[paper_size]
            devmode.Fields |= win32con.DM_PAPERSIZE

        # 设置方向
        orientation = params.get("orientation")
        if orientation and orientation in ORIENTATIONS:
            devmode.Orientation = ORIENTATIONS[orientation]
            devmode.Fields |= win32con.DM_ORIENTATION

        # 设置打印质量
        quality = params.get("quality")
        if quality and quality in QUALITIES:
            devmode.PrintQuality = QUALITIES[quality]
            devmode.Fields |= win32con.DM_PRINTQUALITY

        # 设置双面打印
        duplex = params.get("duplex")
        if duplex and duplex in DUPLEX_MODES:
            devmode.Duplex = DUPLEX_MODES[duplex]
            devmode.Fields |= win32con.DM_DUPLEX

        # 设置份数
        copies = params.get("copies", 1)
        if copies > 1:
            devmode.Copies = copies
            devmode.Fields |= win32con.DM_COPIES

        # 设置颜色模式
        color_mode = params.get("color_mode")
        if color_mode:
            if color_mode == "黑白":
                devmode.Color = win32con.DMCOLOR_MONOCHROME
            else:
                devmode.Color = win32con.DMCOLOR_COLOR
            devmode.Fields |= win32con.DM_COLOR

        # 更新打印机设置
        win32print.DocumentProperties(
            None,
            h_printer,
            printer_name,
            devmode,
            devmode,
            win32con.DM_IN_BUFFER | win32con.DM_OUT_BUFFER,
        )

        return devmode

    except Exception as e:
        logger.warning(f"应用打印机设置失败: {e}")
    finally:
        win32print.ClosePrinter(h_printer)
